﻿using System;
using System.Collections.Generic;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using advadev.CanOpen;


namespace advadev.CanOpen.UnitTests
{

    /// <summary>
    /// Represents the multiplex process data object write transfer unit tests
    /// </summary>
    [TestClass]
    public class MultiplexProcessDataObjectWriteTransferTest : ICanOpenObjectDictionaryContainer
    {

        #region constructor tests
        /// <summary>
        /// Constructor test
        /// </summary>
        [TestMethod]
        public void ConstructorTest()
        {
            // initialization
            byte nodeId = 103;
            ICanOpenValueObject canOpenValueObject = new CanOpenUnsigned8(0xABCD, 0x0E, true, true) { Value = 127 };
            ICanOpenValueObject bigCanOpenValueObject = new CanOpenUnsigned64(0xABCD, 0x0E, true, true) { Value = 127 };
            MultiplexProcessDataObjectWriteTransfer testObject = null;

            // can open value object null parameter
            try
            {
                testObject = new MultiplexProcessDataObjectWriteTransfer(null, MultiplexProcessDataObjectAdressType.SourceAddressing, nodeId, 0, 0x180);
                Assert.Fail();
            }
            catch (ArgumentNullException)
            {
            }
            catch
            {
                Assert.Fail();
            }

            // node id out of range parameter
            try
            {
                testObject = new MultiplexProcessDataObjectWriteTransfer(canOpenValueObject, MultiplexProcessDataObjectAdressType.SourceAddressing, 0, 127, 0x180);
                Assert.Fail();
            }
            catch (ArgumentOutOfRangeException)
            {
            }
            catch
            {
                Assert.Fail();
            }
            try
            {
                testObject = new MultiplexProcessDataObjectWriteTransfer(canOpenValueObject, MultiplexProcessDataObjectAdressType.SourceAddressing, 128, 127, 0x180);
                Assert.Fail();
            }
            catch (ArgumentOutOfRangeException)
            {
            }
            catch
            {
                Assert.Fail();
            }

            // destination node id out of range parameter
            try
            {
                testObject = new MultiplexProcessDataObjectWriteTransfer(canOpenValueObject, MultiplexProcessDataObjectAdressType.DestinationAddressing, nodeId, 128, 0x180);
                Assert.Fail();
            }
            catch (ArgumentOutOfRangeException)
            {
            }
            catch
            {
                Assert.Fail();
            }
        }
        #endregion constructor tests


        #region producer side tests
        /// <summary>
        /// Producer side test
        /// </summary>
        [TestMethod]
        public void ProducerSideTest()
        {
            // initialization
            byte nodeId = 103;
            byte destinationNodeId = 127;
            UInt16 cobId = 0x180;
            ICanOpenValueObject canOpenValueObject = new CanOpenUnsigned32(0xABCD, 0x0E, true, true) { Value = 1000000000 };
            MultiplexProcessDataObjectWriteTransfer testObject = null;

            // test with source addressing
            testObject = new MultiplexProcessDataObjectWriteTransfer(canOpenValueObject, MultiplexProcessDataObjectAdressType.SourceAddressing, nodeId, 0, cobId);
            testObject.Start();
            Assert.AreEqual(false, testObject.TransferComplete);
            Assert.IsNull(testObject.AbortCode);
            Assert.IsNotNull(testObject.PendingSendMessages);
            Assert.AreEqual(1, testObject.PendingSendMessages.Count);
            Assert.AreEqual(0x1E7, testObject.PendingSendMessages[0].CanId);
            Assert.AreEqual(0x67, testObject.PendingSendMessages[0].Byte0);
            Assert.AreEqual(0xCD, testObject.PendingSendMessages[0].Byte1);
            Assert.AreEqual(0xAB, testObject.PendingSendMessages[0].Byte2);
            Assert.AreEqual(0x0E, testObject.PendingSendMessages[0].Byte3);
            Assert.AreEqual(0x00, testObject.PendingSendMessages[0].Byte4);
            Assert.AreEqual(0xCA, testObject.PendingSendMessages[0].Byte5);
            Assert.AreEqual(0x9A, testObject.PendingSendMessages[0].Byte6);
            Assert.AreEqual(0x3B, testObject.PendingSendMessages[0].Byte7);
            Assert.AreEqual((uint)8, testObject.PendingSendMessages[0].Length);
            testObject.ContinueAfterSendingMessages();
            Assert.AreEqual(true, testObject.TransferComplete);
            Assert.IsNull(testObject.AbortCode);
            Assert.IsNotNull(testObject.PendingSendMessages);
            Assert.AreEqual(0, testObject.PendingSendMessages.Count);

            // test with destination addressing
            testObject = new MultiplexProcessDataObjectWriteTransfer(canOpenValueObject, MultiplexProcessDataObjectAdressType.DestinationAddressing, nodeId, destinationNodeId, cobId);
            testObject.Start();
            Assert.AreEqual(false, testObject.TransferComplete);
            Assert.IsNull(testObject.AbortCode);
            Assert.IsNotNull(testObject.PendingSendMessages);
            Assert.AreEqual(1, testObject.PendingSendMessages.Count);
            Assert.AreEqual(0x1E7, testObject.PendingSendMessages[0].CanId);
            Assert.AreEqual(0xFF, testObject.PendingSendMessages[0].Byte0);
            Assert.AreEqual(0xCD, testObject.PendingSendMessages[0].Byte1);
            Assert.AreEqual(0xAB, testObject.PendingSendMessages[0].Byte2);
            Assert.AreEqual(0x0E, testObject.PendingSendMessages[0].Byte3);
            Assert.AreEqual(0x00, testObject.PendingSendMessages[0].Byte4);
            Assert.AreEqual(0xCA, testObject.PendingSendMessages[0].Byte5);
            Assert.AreEqual(0x9A, testObject.PendingSendMessages[0].Byte6);
            Assert.AreEqual(0x3B, testObject.PendingSendMessages[0].Byte7);
            Assert.AreEqual((uint)8, testObject.PendingSendMessages[0].Length);
            testObject.ContinueAfterSendingMessages();
            Assert.AreEqual(true, testObject.TransferComplete);
            Assert.IsNull(testObject.AbortCode);
            Assert.IsNotNull(testObject.PendingSendMessages);
            Assert.AreEqual(0, testObject.PendingSendMessages.Count);

            // test with destination group addressing
            testObject = new MultiplexProcessDataObjectWriteTransfer(canOpenValueObject, MultiplexProcessDataObjectAdressType.DestinationAddressing, nodeId, 0, cobId);
            testObject.Start();
            Assert.AreEqual(false, testObject.TransferComplete);
            Assert.IsNull(testObject.AbortCode);
            Assert.IsNotNull(testObject.PendingSendMessages);
            Assert.AreEqual(1, testObject.PendingSendMessages.Count);
            Assert.AreEqual(0x1E7, testObject.PendingSendMessages[0].CanId);
            Assert.AreEqual(0x80, testObject.PendingSendMessages[0].Byte0);
            Assert.AreEqual(0xCD, testObject.PendingSendMessages[0].Byte1);
            Assert.AreEqual(0xAB, testObject.PendingSendMessages[0].Byte2);
            Assert.AreEqual(0x0E, testObject.PendingSendMessages[0].Byte3);
            Assert.AreEqual(0x00, testObject.PendingSendMessages[0].Byte4);
            Assert.AreEqual(0xCA, testObject.PendingSendMessages[0].Byte5);
            Assert.AreEqual(0x9A, testObject.PendingSendMessages[0].Byte6);
            Assert.AreEqual(0x3B, testObject.PendingSendMessages[0].Byte7);
            Assert.AreEqual((uint)8, testObject.PendingSendMessages[0].Length);
            testObject.ContinueAfterSendingMessages();
            Assert.AreEqual(true, testObject.TransferComplete);
            Assert.IsNull(testObject.AbortCode);
            Assert.IsNotNull(testObject.PendingSendMessages);
            Assert.AreEqual(0, testObject.PendingSendMessages.Count);
        }
        #endregion producer side tests


        #region is multiplex process data object write transfer request tests
        /// <summary>
        /// Is multiplex process data object write transfer request test
        /// </summary>
        [TestMethod]
        public void IsMultiplexProcessDataObjectWriteTransferRequestTest()
        {
            // initialization
            bool isMultiplexProcessDataObjectWriteTransferRequest = false;
            byte nodeId = 103;
            uint mpdoIndex = 0;
            ICanOpenValueObject canOpenValueObject = new CanOpenUnsigned8(0xABCD, 0x0E, true, true) { Value = 127 };
            MultiplexProcessDataObjectWriteTransfer testObject = null;

            // test with valid source addressing received can open message
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(new CanOpenMessage(0x1E7, 0x00, 0xCD, 0xAB, 0x0E, 0x7F, 0x00, 0x00, 0x00), nodeId, this, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
            Assert.AreEqual(true, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNotNull(testObject);

            // test with valid destination addressing received can open message
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(new CanOpenMessage(0x1E7, 0xE7, 0xCD, 0xAB, 0x0E, 0x7F, 0x00, 0x00, 0x00), nodeId, this, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
            Assert.AreEqual(true, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNotNull(testObject);

            // test with not valid received can open message
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(new CanOpenMessage(0x180, 0x00, 0xCD, 0xAB, 0x0E, 0x7F, 0x00, 0x00, 0x00), nodeId, this, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
            Assert.AreEqual(false, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNull(testObject);

            // test with can open object dictionary is null
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(new CanOpenMessage(0x1E7, 0x00, 0xCD, 0xAB, 0x0E, 0x7F, 0x00, 0x00, 0x00), nodeId, null, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
            Assert.AreEqual(false, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNull(testObject);

            // test with received can open message is null
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(null, nodeId, this, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
            Assert.AreEqual(false, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNull(testObject);

            // test with not matching destination node id
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(new CanOpenMessage(0x1E7, 0xE8, 0xCD, 0xAB, 0x0E, 0x7F, 0x00, 0x00, 0x00), nodeId, this, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
            Assert.AreEqual(false, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNull(testObject);

            // test with COB-ID parameter list is null
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(new CanOpenMessage(0x1E7, 0x00, 0xCD, 0xAB, 0x0E, 0x7F, 0x00, 0x00, 0x00), nodeId, this, out testObject, out mpdoIndex, null);
            Assert.AreEqual(false, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNull(testObject);

            // test with destination addressing and not matching node id
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(new CanOpenMessage(0x268, 0x00, 0xCD, 0xAB, 0x0E, 0x7F, 0x00, 0x00, 0x00), nodeId, this, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
            Assert.AreEqual(false, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNull(testObject);

            // test with destination addressing and node id 0
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(new CanOpenMessage(0x267, 0x00, 0xCD, 0xAB, 0x0E, 0x7F, 0x00, 0x00, 0x00), 0, this, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
            Assert.AreEqual(false, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNull(testObject);

            // test with invalid node id
            try
            {
                isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(new CanOpenMessage(0x1E7, 0x00, 0xCD, 0xAB, 0x0E, 0x7F, 0x00, 0x00, 0x00), 128, this, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
                Assert.Fail();
            }
            catch (ArgumentOutOfRangeException)
            {
            }
            catch
            {
                Assert.Fail();
            }
        }
        #endregion is multiplex process data object write transfer request tests


        #region consumer side tests
        /// <summary>
        /// Consumer side test
        /// </summary>
        [TestMethod]
        public void ComsumerSideTest()
        {
            // initialization
            bool isMultiplexProcessDataObjectWriteTransferRequest = false;
            byte nodeId = 100;
            uint mpdoIndex = 0;
            byte destinationNodeId = 103;
            UInt16 cobId = 0x180;
            CanOpenUnsigned32 canOpenValueObject = new CanOpenUnsigned32(0x0032, 0x01, true, true);
            CanOpenUnsigned32 unkownCanOpenValueObject = new CanOpenUnsigned32(0x1032, 0x01, true, true);
            MultiplexProcessDataObjectWriteTransfer testObject = null;
            MultiplexProcessDataObjectWriteTransfer producerTransfer = null;

            // test with source addressing
            canOpenValueObject.Value = 1000000000;
            _canOpenUnsigned32.Value = 0;
            producerTransfer = new MultiplexProcessDataObjectWriteTransfer(canOpenValueObject, MultiplexProcessDataObjectAdressType.SourceAddressing, nodeId, 0, cobId);
            producerTransfer.Start();
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(producerTransfer.PendingSendMessages[0], destinationNodeId, this, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
            Assert.AreEqual(true, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNotNull(testObject);
            testObject.Start();
            Assert.AreEqual(canOpenValueObject.Value, _canOpenUnsigned32.Value);
            Assert.AreEqual((uint)0, mpdoIndex);
            Assert.AreEqual(true, testObject.TransferComplete);
            Assert.IsNull(testObject.AbortCode);
            Assert.IsNotNull(testObject.PendingSendMessages);
            Assert.AreEqual(0, testObject.PendingSendMessages.Count);

            // test with destination addressing
            canOpenValueObject.Value = 1000000000;
            _canOpenUnsigned32.Value = 0;
            producerTransfer = new MultiplexProcessDataObjectWriteTransfer(canOpenValueObject, MultiplexProcessDataObjectAdressType.DestinationAddressing, nodeId, destinationNodeId, cobId);
            producerTransfer.Start();
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(producerTransfer.PendingSendMessages[0], destinationNodeId, this, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
            Assert.AreEqual(true, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNotNull(testObject);
            testObject.Start();
            Assert.AreEqual(canOpenValueObject.Value, _canOpenUnsigned32.Value);
            Assert.AreEqual((uint)0, mpdoIndex);
            Assert.AreEqual(true, testObject.TransferComplete);
            Assert.IsNull(testObject.AbortCode);
            Assert.IsNotNull(testObject.PendingSendMessages);
            Assert.AreEqual(0, testObject.PendingSendMessages.Count);

            // test with unkown can open object
            unkownCanOpenValueObject.Value = 1000000000;
            _canOpenUnsigned32.Value = 0;
            producerTransfer = new MultiplexProcessDataObjectWriteTransfer(unkownCanOpenValueObject, MultiplexProcessDataObjectAdressType.SourceAddressing, nodeId, 0, cobId);
            producerTransfer.Start();
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(producerTransfer.PendingSendMessages[0], destinationNodeId, this, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
            Assert.AreEqual(true, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNotNull(testObject);
            testObject.Start();
            Assert.AreEqual((ulong)0, _canOpenUnsigned32.Value);
            Assert.AreEqual((uint)0, mpdoIndex);
            Assert.AreEqual(true, testObject.TransferComplete);
            Assert.IsNull(testObject.AbortCode);
            Assert.IsNotNull(testObject.PendingSendMessages);
            Assert.AreEqual(0, testObject.PendingSendMessages.Count);

            // test with not active PDO
            _receiveCobId1.Value += 0x80000000;
            canOpenValueObject.Value = 1000000000;
            _canOpenUnsigned32.Value = 0;
            producerTransfer = new MultiplexProcessDataObjectWriteTransfer(canOpenValueObject, MultiplexProcessDataObjectAdressType.SourceAddressing, nodeId, 0, cobId);
            producerTransfer.Start();
            isMultiplexProcessDataObjectWriteTransferRequest = MultiplexProcessDataObjectWriteTransfer.IsMultiplexProcessDataObjectWriteTransfer(producerTransfer.PendingSendMessages[0], destinationNodeId, this, out testObject, out mpdoIndex, new List<CanOpenUnsigned> { _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4 });
            Assert.AreEqual(false, isMultiplexProcessDataObjectWriteTransferRequest);
            Assert.IsNull(testObject);
            Assert.AreEqual((ulong)0, _canOpenUnsigned32.Value);
            Assert.AreEqual((uint)0, mpdoIndex);
            _receiveCobId1.Value -= 0x80000000;
        }
        #endregion consumer side tests


        #region ICanOpenObjectDictionaryContainer
        /// <summary>for internal use</summary>
        private CanOpenUnsigned32 _canOpenUnsigned32 = new CanOpenUnsigned32(0x0032, 0x01, true, true);
        /// <summary>for internal use</summary>
        private CanOpenUnsigned32 _receiveCobId1 = new CanOpenUnsigned32(0x1400, 0x01, true, true) { Value = 0x180 };
        /// <summary>for internal use</summary>
        private CanOpenUnsigned32 _receiveCobId2 = new CanOpenUnsigned32(0x1401, 0x01, true, true) { Value = 0x280 };
        /// <summary>for internal use</summary>
        private CanOpenUnsigned32 _receiveCobId3 = new CanOpenUnsigned32(0x1402, 0x01, true, true) { Value = 0x380 };
        /// <summary>for internal use</summary>
        private CanOpenUnsigned32 _receiveCobId4 = new CanOpenUnsigned32(0x1403, 0x01, true, true) { Value = 0x480 };
        /// <summary>for internal use</summary>
        private CanOpenUnsigned32 _receiveSourceAddressingMapping = new CanOpenUnsigned32(0x1600, 0x00, true, true) { Value = 0xEF };
        /// <summary>for internal use</summary>
        private CanOpenUnsigned32 _receiveDestinationMapping = new CanOpenUnsigned32(0x1601, 0x00, true, true) { Value = 0xFF };
        /// <summary>for internal use</summary>
        private List<ICanOpenValueObject> _objects;

        /// <summary>
        /// Gets the node id of the can open object dictionary container
        /// </summary>
        byte ICanOpenObjectDictionaryContainer.NodeId
        {
            get
            {
                return 103;
            }
        }

        /// <summary>
        /// Gets the can open object with the given index as can open data field
        /// object
        /// </summary>
        /// <param name="index">
        /// Index of the can open object to get
        /// </param>
        /// <returns>
        /// The can open object with the given index as data field object or
        /// null
        /// </returns>
        ICanOpenDataFieldObject ICanOpenObjectDictionaryContainer.GetCanOpenObject(UInt16 index)
        {
            return null;
        }

        /// <summary>
        /// Gets the can open object with the given index and sub index as can
        /// open value object
        /// </summary>
        /// <param name="index">
        /// Index of the can open object to get
        /// </param>
        /// <param name="subIndex">
        /// Sub index of the can open object to get
        /// </param>
        /// <returns>
        /// The can open object with the given index and sub index as value
        /// object or null
        /// </returns>
        ICanOpenValueObject ICanOpenObjectDictionaryContainer.GetCanOpenObject(UInt16 index, byte subIndex)
        {
            if (_objects == null)
            {
                _objects = new List<ICanOpenValueObject>() { _canOpenUnsigned32, _receiveCobId1, _receiveCobId2, _receiveCobId3, _receiveCobId4, _receiveSourceAddressingMapping, _receiveDestinationMapping };
            }
            foreach (ICanOpenValueObject canOpenObject in _objects)
            {
                if (canOpenObject.Index == index && canOpenObject.SubIndex == subIndex)
                {
                    return canOpenObject;
                }
            }
            return null;
        }
        #endregion ICanOpenObjectDictionaryContainer

    }

}
